package com.longcai.cm.api.api4order;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.alipay.api.internal.util.AlipaySignature;
import com.google.zxing.WriterException;
import com.longcai.cm.api.request.CreateOrderParam;
import com.longcai.cm.api.request.OrderCommentParam;
import com.longcai.cm.api.response.OrderInfo;
import com.longcai.cm.auth.Login;
import com.longcai.cm.auth.LoginUser;
import com.longcai.cm.bean.R;
import com.longcai.cm.common.utils.StringUtil;
import com.longcai.cm.common.utils.StringUtils;
import com.longcai.cm.delay.OrderDelay;
import com.longcai.cm.domain.*;
import com.longcai.cm.domain.vm.RefundApplyVm;
import com.longcai.cm.exception.SysRuntimeException;
import com.longcai.cm.moudules.system.bean.DicCode;
import com.longcai.cm.moudules.system.service.SysDicCodeService;
import com.longcai.cm.pay.ali.*;
import com.longcai.cm.pay.wx.WXPayUtil;
import com.longcai.cm.pay.wx.WxPayApp;
import com.longcai.cm.pay.wx.domain.WxOrderParam;
import com.longcai.cm.service.*;
import com.longcai.cm.utils.GeoCalculator;
import com.longcai.cm.utils.QrCodeUtil;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.partnerpayments.app.model.Transaction;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.BeanUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;
import java.util.concurrent.DelayQueue;

/**
 * @author libingxing
 */
@RestController
@Api(tags = "订单相关API")
@RequestMapping("/api/order")
public class ApiOrderController {

    @Resource
    private JaOrderService orderService;

    @Resource
    private JaShopService shopService;

    @Resource
    private JaOrderRefundApplyService orderRefundApplyService;

    @Resource
    private JaOrderCommentService orderCommentService;

    @Resource
    private SysDicCodeService sysDicCodeService;

    @Resource
    private JaMemberVipService memberVipService;

    @Resource
    private JaMemberCouponsService memberCouponsService;

    @Resource
    private JaMemberAssetService memberAssetService;

    @Resource
    private JaOrderRefundApplyService refundApplyService;

    @Resource
    private DelayQueue<OrderDelay> orderDelayQueue;

    @Login
    @PostMapping("/createOrder")
    @ApiOperation("创建订单")
    @Transactional(rollbackFor = Exception.class)
    public R<Object> createOrder(
            @ApiIgnore @LoginUser FaMember member,
            CreateOrderParam param
    ) {
        JaOrder order = new JaOrder();
        BeanUtils.copyProperties(param, order);
        BigDecimal reduceAmount = new BigDecimal("0");
//        积分抵扣
        if (param.getIntegral() != null && param.getIntegral() > 0) {
            final String integralRatio = sysDicCodeService.getTextValueByCode(DicCode.INTEGRAL_RATIO);
            final BigDecimal ratio = new BigDecimal(integralRatio);
            final BigDecimal integral = new BigDecimal(param.getIntegral());
            memberAssetService.updateMemberAssetIntegral(member.getId() + "", new BigDecimal(param.getIntegral()), 0, order.getShopName() + "，服务购买积分抵扣");
            final BigDecimal reduceAmountIntegral = integral.divide(ratio, 2, RoundingMode.HALF_UP);
            order.setReduceAmountIntegral(reduceAmountIntegral);
            reduceAmount = reduceAmountIntegral;
        }
//        优惠券
        if (StringUtils.isNotBlank(order.getCouponId())) {
            final BigDecimal reduceAmountCoupon = memberCouponsService.changeUseStatusToUse(param.getGoodId(), order.getCouponId());
            order.setReduceAmountCoupon(reduceAmountCoupon);
            if (reduceAmountCoupon.doubleValue() > 0) {
                reduceAmount = reduceAmount.add(reduceAmountCoupon);
            }else {
                reduceAmount = order.getOrderAmount();
            }
        }
        order.setOrderType(param.getGoodType());
        order.setReduceAmount(reduceAmount);
        order.setContactMobile(member.getMobile());
        orderService.createOrder(order, member.getId(), memberVipService.isVip(member.getId()));
        orderDelayQueue.add(new OrderDelay(order.getId(), 30));
        return R.success(order);
    }

    @Login
    @PostMapping("/orderInfo")
    @ApiOperation("订单详情")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "id", name = "id", dataType = "String", required = true),
            @ApiImplicitParam(value = "经度", name = "lng", dataType = "String", required = true),
            @ApiImplicitParam(value = "纬度", name = "lat", dataType = "String", required = true),
    })
    public R<OrderInfo> orderInfo(
            String id, @ApiIgnore @LoginUser FaMember member, Double lng, Double lat
    ) throws IOException, WriterException {
        final OrderInfo orderInfo = new OrderInfo();
        final JaOrder order = orderService.getById(id);
        if (order == null) {
            return R.error("内容走丢了");
        }
        orderInfo.setMobile(member.getMobile());
        orderInfo.setWxname(member.getWxname());
        orderInfo.setExpectServiceTime(order.getExpectServiceTime());
        orderInfo.setOrder(order);
        final JaShop shop = shopService.getById(order.getShopId());
        if (shop != null && shop.getLng() != null && shop.getLat() != null) {
            shop.setDistance(GeoCalculator.calculateDistance(lat, lng, new BigDecimal(shop.getLat()).doubleValue(), new BigDecimal(shop.getLng()).doubleValue()));
        }
        orderInfo.setShop(shop);
        orderInfo.setRefundApply(orderRefundApplyService.lambdaQuery().eq(JaOrderRefundApply::getOrderId, order.getId()).one());
        if (order.getOrderStatus() == 0) {
            final DateTime startTime = DateUtil.parse(order.getCreateTime());
            orderInfo.setPayLeftTime(Math.max((startTime.offset(DateField.MINUTE, 30).getTime() - DateUtil.current()) / 1000, 0));
        }
        if (order.getOrderStatus() == 1) {
            orderInfo.setCheckQrCode(QrCodeUtil.generateQRCodeImage(order.getId(), 100, 100));
        }
        return R.success(orderInfo);
    }

    @Login
    @PostMapping("/cancelOrder")
    @ApiOperation("取消订单")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "订单id", name = "id", dataType = "int", required = true),
            @ApiImplicitParam(value = "取消原因", name = "cancelReason", dataType = "int", required = true)
    })
    public R<Object> cancelOrder(
            @ApiIgnore @LoginUser FaMember member,
            String id, String cancelReason
    ) {
        final JaOrder order = orderService.getById(id);
        if (order.getOrderStatus() != 0) {
            return R.error("已支付，请申请退款");
        }
//        积分抵扣
        if (order.getIntegral() != null && order.getIntegral() > 0) {
            memberAssetService.updateMemberAssetIntegral(member.getId() + "", new BigDecimal(order.getIntegral()), 1, order.getShopName() + order.getGoodName() + "，订单取消积分退还");
        }
//        优惠券
        if (StringUtils.isNotBlank(order.getCouponId())) {
            memberCouponsService.changeUseStatusToCancel(order.getCouponId());
        }
        order.setOrderStatus(4);
        order.setCancelReason(cancelReason);
        orderService.updateById(order);
        return R.success();
    }

    @Login
    @PostMapping("/orderComment")
    @ApiOperation("订单评价")
    public R<Object> orderComment(
            @ApiIgnore @LoginUser FaMember member,
            OrderCommentParam param
    ) {
        final JaOrderComment old = orderCommentService.lambdaQuery().eq(JaOrderComment::getMemberId, member.getId()).eq(JaOrderComment::getOrderId, param.getOrderId()).one();
        if (old != null) {
            return R.error("请勿重复评价");
        }
        final JaOrder order = orderService.getById(param.getOrderId());
        if (order == null) {
            return R.error("订单走丢了");
        }
        final JaShop shop = shopService.getById(order.getShopId());
        JaOrderComment orderComment = new JaOrderComment();
        BeanUtils.copyProperties(param, orderComment);
        orderComment.setName(member.getName());
        orderComment.setAvatar(member.getAvatar());
        orderComment.setId(StringUtil.getUuid());
        orderComment.setMemberId(member.getId());
        orderComment.setCreateTime(DateUtil.now());
        orderComment.setShopId(order.getShopId());
        orderComment.setShopName(shop.getName());
        orderComment.setGoodId(order.getGoodId());
        orderComment.setGoodName(order.getGoodName());
        orderCommentService.save(orderComment);
        shopService.updateComment(shop);
        order.setOrderStatus(3);
        orderService.updateById(order);
        return R.success();
    }

    @Login
    @PostMapping("/refundOrder")
    @ApiOperation("订单退款")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "订单id", name = "orderId", dataType = "int", required = true)
    })
    public R<Object> refundOrder(
            RefundApplyVm applyVm,
            @ApiIgnore @LoginUser FaMember user
    ) {
        JaOrderRefundApply one = refundApplyService.lambdaQuery().eq(JaOrderRefundApply::getOrderId, applyVm.getOrderId()).one();
        if (one != null) {
            if (one.getStatus() == 2) {
                one.setStatus(0);
                one.setRefundCount(one.getRefundCount() + 1);
                refundApplyService.updateById(one);
                return R.success("已重新提交");
            } else {
                return R.error("申请已提交，请勿重复申请");
            }
        }
        one = new JaOrderRefundApply();
        BeanUtils.copyProperties(applyVm, one);
        final JaOrder order = orderService.getById(applyVm.getOrderId());
        one.setId(StringUtil.getUuid());
        one.setMemberId(user.getId());
        one.setStatus(0);
        one.setCreateTime(DateUtil.now());
        one.setRefundNum(order.getOrderNum().replace("C", "R"));
        one.setRefundAmount(order.getPayAmount());
        refundApplyService.save(one);
        order.setOrderStatus(5);
        orderService.updateById(order);
        return R.success("申请已提交，请等待审核");
    }

    @Login
    @PostMapping("/payOrder")
    @ApiOperation("订单支付")
    @ApiImplicitParams({
            @ApiImplicitParam(value = "订单id", name = "orderId", dataType = "int", required = true),
            @ApiImplicitParam(value = "支付类型 1微信 2支付宝", name = "payType", dataType = "int", required = true)
    })
    public R<String> payOrder(
            String orderId, Integer payType
    ) throws Exception {
        switch (payType) {
            case 1:
                return payOrderWxApp(orderId);
            case 2:
                return payOrderAli(orderId);
            default:
                return R.error("支付失败");
        }
    }

    private R<String> payOrderWxApp(String orderId) {
        final PayOrderParam orderParam = getPayParam(orderId);
        WxOrderParam param = new WxOrderParam();
        param.setDescription(orderParam.getSpecifyName());
        param.setOrderNum(orderParam.getOrderNum());
        param.setAmount(orderParam.getPayAmount().doubleValue());
        param.setAttach(orderId);
        param.setNotifyUrl("/api/order/wxAppRecharge");
        return R.success(WxPayApp.createOrderApp(param));
    }

    private R<String> payOrderAli(String orderId) throws Exception {
        final PayOrderParam param = getPayParam(orderId);
        return R.success(
                AliPayApi.createOrderAliPay(
                        param.getSpecifyName(), param.getOrderNum(), param.getPayAmount().toString(),
                        orderId, "/api/order/aliRecharge"
                )
        );
    }

    public PayOrderParam getPayParam(String orderId) {
        final PayOrderParam param = new PayOrderParam();
        final JaOrder order = orderService.getById(orderId);
        param.setPayAmount(order.getPayAmount());
        param.setMemberId(order.getMemberId() + "");
        param.setOrderNum(order.getOrderNum());
        param.setSpecifyName(order.getGoodName());
        if (param.getPayAmount().doubleValue() <= 0) {
            throw new SysRuntimeException("金额错误");
        }
        return param;
    }

    @Login
    @PostMapping("/addCoupons")
    @ApiOperation("addCoupons")
    public R<Object> addCoupons(
            @ApiIgnore @LoginUser FaMember user, String couponId
    ) {
        memberCouponsService.addCoupons(couponId, 10, user.getId());
        return R.success();
    }

    /**
     * 阿里支付回调
     */
    @RequestMapping("/aliRecharge")
    @Transactional(rollbackFor = Exception.class)
    public String aliRecharge(HttpServletRequest request) throws Exception {
        Map<String, String> params = AliPay.convertRequestParamsToMap(request);
        System.out.println("回调接受到的参数：" + params);
        // 调用SDK验证签名
        boolean signVerified = AlipaySignature.rsaCheckV1(params, AliPayConfig.ALIPAY_PUBLIC_KEY, AliPayConfig.CHARSET, AliPayConfig.SIGNTYPE);
        if (signVerified) {
            AliPayNotifyParam aliParams = AliPay.buildAliPayNotifyParam(params);
            if (TradeStatus.TRADE_SUCCESS.getValue().equalsIgnoreCase(aliParams.getTradeStatus())) {
                //业务信息
                final String orderId = aliParams.getPassBackParams();
                orderService.tradeFinish(orderId, aliParams.getTradeNo(), aliParams.getTotalAmount(), 2);
                return AliPay.SUCCESS;
            } else {
                //记录错误
                try {
                    System.err.println("签名错误");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } else {
            System.out.println("验签失败！");
            return AliPay.FAILURE;
        }
        return AliPay.FAILURE;
    }

    /**
     * 微信支付回调
     */
    @RequestMapping("/wxAppRecharge")
    @Transactional(rollbackFor = Exception.class)
    public ResponseEntity<Object> wxAppRecharge(
            HttpServletRequest request
    ) {
//        构造 RequestParam
        RequestParam requestParam = WXPayUtil.getRequestParam(request);
//        初始化 RSAAutoCertificateConfig
        NotificationConfig config = WxPayApp.getNotificationConfig();
//        初始化 NotificationParser
        NotificationParser parser = new NotificationParser(config);
        ResponseEntity.BodyBuilder errorMsg = wxPayBusiness(requestParam, parser);
        if (errorMsg != null) {
            return errorMsg.build();
        }
//        处理成功，返回 200 OK 状态码
        return ResponseEntity.status(HttpStatus.OK).build();
    }

    private ResponseEntity.BodyBuilder wxPayBusiness(RequestParam requestParam, NotificationParser parser) {
        try {
//            以支付通知回调为例，验签、解密并转换成 Transaction
            Transaction transaction = parser.parse(requestParam, Transaction.class);
            if (Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())) {
                final BigDecimal total = new BigDecimal(transaction.getAmount().getTotal()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
//                支付成功
                final String orderId = transaction.getAttach();
                orderService.tradeFinish(orderId, transaction.getTransactionId(), total, 1);
            } else {
                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
            }
        } catch (ValidationException e) {
            // 签名验证失败，返回 401 UNAUTHORIZED 状态码
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED);
        }
        return null;
    }

}
